/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Communication;

import Request.RequestRdyArg;
import Common.InfoLogger;
import Response.*;
import Request.*;
import java.io.*;
import java.util.*;
import java.net.*;
import AccManager.UserProfile;
import java.util.ArrayList;
import Events.*;

/**
 *
 * @author lcy
 */

public class RequestThread extends Thread implements SendRespHandler{

    private InfoLogger _debugger;
    private List<RequestEndListener>_listeners;
    private List<RequestReadyHandler> _r_rdy_handlers;
    private NetDataResponse _response;

    private BufferedReader in_stream;
    private DataOutputStream out_stream;

    private InputStream in_s;
    private OutputStream out_s ;
    private ObjectInput in_sock ;
    private ObjectOutput out_sock ;

    Socket _main_socket;

    public RequestThread(Socket new_request){
        _listeners = new ArrayList<RequestEndListener>();
        _r_rdy_handlers = new ArrayList<RequestReadyHandler>();

        _main_socket = new_request;
        _response = null;

        InitCommComponents();
    }

    private void InitCommComponents()
    {
        _debugger = new InfoLogger();
    }

    public void run(){

        try {
            
	    ProcessRequestOBJ();

	} catch (Exception e) {
            e.printStackTrace();
	    _debugger.LogInfo("Request thread process RequestOBJ exception");
            return;
	}

        _debugger.LogInfo("ServerThread: ending request...");
        _fireRequestEndEvent(); //Inform the transceiver to do proper work
        _listeners.clear();

        /*Version 0.1 close the connection gracefully*/
        try{
            _debugger.LogInfo("ServerThread: closing socket...");
            out_sock.close();
            in_sock.close();
            in_s.close();
            out_s.close();
            _main_socket.close();
        }catch(IOException ex){
            //ex.printStackTrace();
            _debugger.LogInfo("Server thread: cannot close socket.");
        }catch(NullPointerException nullex){
            _debugger.LogInfo("Server thread: one or more channels have closed");
        }
        
    }

    private boolean ProcessRequestOBJ(){

        try{
            in_s =  _main_socket.getInputStream();
            out_s = _main_socket.getOutputStream();

            try{
                in_sock = new ObjectInputStream(in_s);
                out_sock = new ObjectOutputStream(out_s);
            }catch(IOException ex){
                _debugger.LogInfo("Server thread: Object in/out not available");
            }

            while(true){
                try{

                    //Blocking read
                    Request request = (Request)in_sock.readObject();

                    //This is a request for close tcp connection;
                    if(request.GetRequestType() == RequestType.CLOSE_CONN){                       
                        break;
                    }


                    NetDataProcessor data_processor = new NetDataProcessor(request);

                    CreateResponse(request.GetRequestType());

                    _debugger.LogInfo("RequestThread: request received");
                    data_processor.FormResponse(_response);

                    if(_response.IsDataFormatValid()){
                        //Inform others that we need further
                        //help to get the complete response;
                        _debugger.LogInfo("RequestThread: request valid");
                        _fireRequestReadyEvent();
                    }
                    else{
                        //Inform the client that the request format is not
                        //correct
                        _debugger.LogInfo("RequestThread: request NOT valid");
                        out_sock.writeObject(_response);
                    }

                }catch(ClassNotFoundException ex){
                    ex.printStackTrace();
                    return false;
                }catch(EOFException ex3){
                    //The client might hav accientally leave the
                    _debugger.LogInfo("RequestThread: client left connection EOF");
                    return false;
                }
                catch(SocketException ex2){
                    return false;
                }catch(NullPointerException ex){
                    _debugger.LogInfo("Server thread: client close connection.");
                    return false;
                }

            }
         
        }catch(IOException e){
            e.printStackTrace();
            return false;
        }

        return true;
    }

    private boolean CreateResponse(RequestType r_type){
        switch(r_type){
            
            case LOGIN:
            case REGISTRATION:
                try{
                    _response = new NetDataLRResp();
                }catch(UnknownHostException ex){
                    return false;
                }
                return true;
                
            case USERLIST:
                List<UserProfile> profile_list = new ArrayList<UserProfile>();
                _response = new NetDataUserListResp(profile_list);
                _debugger.LogInfo("RequestThread: profile list created");

                return true;
        }

        return true;

    }

    /* For request end event */
    public Socket GetClientAddr(){

        return _main_socket;
    }

    public synchronized void addRequestEndListener( RequestEndListener l ) {
        _listeners.add( l );
    }

    public synchronized void removeRequestEndListener( RequestEndListener l ) {
        _listeners.remove( l );
    }

    public synchronized void _fireRequestEndEvent() {

        RequestEndEvent request_end = new RequestEndEvent(this,_main_socket);

        Iterator listeners = _listeners.iterator();
        
        while(listeners != null && listeners.hasNext() ) {
            RequestEndListener handler = ((RequestEndListener) listeners.next());
            handler.EndingRequest(request_end);
            _debugger.LogInfo("RequestThread: closing thread...");
        }
    }

    public synchronized void addRequestReadyHandler( RequestReadyHandler l ) {
        _r_rdy_handlers.add( l );
    }

    public synchronized void removeRequestReadyHandler( RequestReadyHandler l ) {
        _r_rdy_handlers.remove( l );
    }

    public synchronized void _fireRequestReadyEvent() {

        RequestRdyArg arg = new RequestRdyArg(_response);
        arg.SetRequestId(this.getId()); // Fill in request id;
        RequestResultReadyEvent request_ready =
                new RequestResultReadyEvent(this,arg);

        Iterator listeners = _r_rdy_handlers.iterator();

        while(listeners != null && listeners.hasNext() ) {
            RequestReadyHandler handler = ((RequestReadyHandler) listeners.next());
            handler.HandleResponse(request_ready);
            _debugger.LogInfo("RequestThread: one handler executed...");
        }
    }

    public void RespSendHandler(SendRespEvent eve){
        //TODO send tcp response to the remote client
        SendRespArg arg = eve.GetArg();
        long id = arg.GetRequestId();
        NetDataResponse resp = arg.GetResponse();
        
        if(id == this.getId()){
            try{
                switch(resp.GetRespType()){
                    case USERLIST:
                        out_sock.writeObject((NetDataUserListResp)resp);
                        break;
                    default:
                        out_sock.writeObject(resp);
                        break;

                }
                out_sock.flush();
            }catch(IOException ex){
                //Do nothing;
            }
        }
    }
}
